package nl.nhl.idp;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.PorterDuff.Mode;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.ToggleButton;
import android.widget.ViewFlipper;
import nl.nhl.idp.Views.*;
import nl.nhl.idp.Views.Charts.DataPoint;
import nl.nhl.idp.Views.Charts.DataSet;
import nl.nhl.idp.Views.Charts.SensorChart;
import nl.nhl.idp.Views.StatusView.StatusListView;
import nl.nhl.idp.Components.*;
import nl.nhl.idp.Components.Autonomous.AutonomousEngine;
import nl.nhl.idp.Components.Database.DatabaseHelper;
import nl.nhl.idp.Components.Database.SensorData;
import nl.nhl.idp.Components.Database.Session;
import nl.nhl.idp.Components.Network.DataServer;
import nl.nhl.idp.Components.Network.NetEvent;
import nl.nhl.idp.Components.Network.NetListener;
import nl.nhl.idp.Components.Network.NetworkController;
import nl.nhl.idp.Components.Network.ProtocolEncoder;
import nl.nhl.idp.Components.Network.ProtocolOpcodes;

/**
 * @author Mark van der Wal
 */
public class AmfibiControllerActivity extends Activity
{
	private final String DEFAULT_IP = "141.252.29.41";
	private final int DEFAULT_PORT = 2000;
	private final int DEFAULT_NAVCOLOR = 0xFFDDDDDD;
	private final int SELECTED_NAVCOLOR = 0xFFFF0000;
	
	// database
	DatabaseHelper idpDatabase;
	Session idpSession;
	SensorData currentSensorData;
	
	DataServer mDataServer;
	
	Context context;
	
	//input Interval
	NetworkInput networkInput;
	
    private long mCurrentTime;
    private long mLastTime;
    private long mDeltaTime;
    private long mInputTimer;
	private final int INPUT_INTERVAL = 100;
	
	byte steerValue;
	byte driveValue;
	
	private boolean useAcceleroSteering = false;
	private boolean AutoMode = false;
	
	private ViewFlipper mainFlipper;
	
	Animation flipup_in;
	Animation flipup_out;
	Animation flipdown_in;
	Animation flipdown_out;
	Animation flipleft_in;
	Animation flipleft_out;
	Animation flipright_in;
	Animation flipright_out;
	
	Context mContext;
	
	//#### DASHBOARD ####
		JoystickView vJoyStick;
		SteeringView vSteeringWheel;
		TiltView tiltView;
		StatusListView statusListView;
	
		BarView barAccuSpanning;
		BarView barMotorSpanning;
	
		Accelerometer acceleroHandler;
		MainMenuView mMainView;
	
		MazeSensorView mazeSensorView;
		
		AutonomousEngine autonomousEngine;
		
			//#### TOGGLE BUTTONS ####
				StopButtonView button_EmergencyStop;
				ZToggleButton toggleButton_Mode;
				ZToggleButton toggleButton_Waterland;
				ZToggleButton toggleButton_Ring;
				ZToggleButton toggleButton_AcceleroSteer;
			//###END TOGGLE BUTTONS ###
		
			//#### TIMER ####
				ToggleButton timer_StartStopToggleButton;
				Button timer_LapButton;
				TextView timer_CurrentTimeText;
				TextView timer_LapTimeText;
				Timer timer;
				TimerTickListener timerTickTask;
			//#### END TIMER ####

	//#### END DASHBOARD ####
	
	//### REPLAY ###
		Dialog sessionDialog;
		ListView sessionListView;
		
		private ViewFlipper viewFlipper;
		
		//statusView
		private StatusListView statusView;
		
		//Charts
		private SensorChart sensorChart_Angle;
		private SensorChart sensorChart_Battery;
		private SensorChart sensorChart_Engine;
		private SensorChart sensorChart_Maze;
		private DataSet data_Angle;
		private DataSet data_Battery;
		private DataSet data_Engine_Voltage;
		private DataSet data_Engine_Current;
		private DataSet data_MazeForward;
		private DataSet data_MazeLeft;
		private DataSet data_MazeRight;
	//### END REPLAY ###
	
	// Network
	NetworkController mNetworkController;

	// Network used views
	TextView wifiCon;
	TextView vehicleCon;
	TextView serverCon;

	ProgressDialog conDialog;
	AlertDialog alertDialog;
	
	Dialog mDialog;
	EditText IpInput;
	EditText port;
	
	String ipAddress;
	String macAddress;
	int portNumber;
	
	Dialog mSettingsDialog;
	EditText settingsIP;
	EditText settingsPort;
	EditText settingsMac;
	

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		this.context = this;
		setContentView(R.layout.dashboard_control);
		
		getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
		
		InitViews();
		
		// netwerk stuff
		ipAddress = "";
		portNumber = 0;
		mContext = this;
		
		// database
		idpDatabase = new DatabaseHelper(this);
		idpSession = null;
		currentSensorData = new SensorData();
		
		// replay
		sessionDialog = new Dialog(this);
		sessionDialog.setTitle("Sessions");
		sessionDialog.setContentView(R.layout.sessionlistview);
		sessionListView = (ListView)sessionDialog.findViewById(R.id.sessionList);
		sessionListView.setOnItemClickListener(new OnItemClickListener()
		{
			public void onItemClick(AdapterView<?> adapter, View v, int pos,
					long id) 
			{
				// get session selected
				Session session = ((SessionAdapter)adapter.getAdapter()).GetItem(pos);
				
				// get sensor data belonging to this session
				List<SensorData> sensorData = idpDatabase.GetSensorData(session);
				
				//crude check without handling, prevents crash when loading bad session
				if(sensorData != null)
				{
					//clear chart data
					data_Angle.Clear();
					data_Battery.Clear();
					data_Engine_Voltage.Clear();
					data_Engine_Current.Clear();
					data_MazeForward.Clear();
					data_MazeLeft.Clear();
					data_MazeRight.Clear();
					
					//reset chart pan/zoom values
					sensorChart_Angle.Reset();
					sensorChart_Battery.Reset();
					sensorChart_Engine.Reset();
					sensorChart_Maze.Reset();
					
					statusView.SetItem("Start Time", session.getStartTime());
					statusView.SetItem("End Time", session.getEndTime());
					
					statusView.SetItem("Reading Count", sensorData.size(), "readings");
					
					//temp vars for max values
					int maxEngineCurrent = Integer.MIN_VALUE;
					
					
					// implement graph stuff for replay
					for(int i=0;i<sensorData.size();i++)
					{
						//get max values
						if(sensorData.get(i).getMotorstroom() > maxEngineCurrent)
							maxEngineCurrent = sensorData.get(i).getMotorstroom(); 
						
						//if last reading
						if(i == sensorData.size()-1)
						{
							//put data in statusview
			                statusView.SetItem("Latest Angle Reading", sensorData.get(i).getHellingshoek(), "degrees");
			                statusView.SetItem("Latest Battery Voltage", sensorData.get(i).getAccuspanning(), "volt");
			                statusView.SetItem("Latest Engine Voltage", sensorData.get(i).getMotorspanning(),"volt");
			                
			                statusView.SetItem("Latest Maze Forward Sensor", sensorData.get(i).getMazeSensorForward(),"cm");
			                statusView.SetItem("Latest Maze Left Sensor", sensorData.get(i).getMazeSensorLeft(),"cm");
			                statusView.SetItem("Latest Maze Right Sensor", sensorData.get(i).getMazeSensorRight(),"cm");
						}
	                    
	                    // put data in charts
	                    data_Angle.Add(new DataPoint(i, sensorData.get(i).getHellingshoek()));
	                    data_Battery.Add(new DataPoint(i, sensorData.get(i).getAccuspanning()));
	                    data_Engine_Voltage.Add(new DataPoint(i, sensorData.get(i).getMotorspanning()));
	                    data_Engine_Current.Add(new DataPoint(i, sensorData.get(i).getMotorstroom()));
	                    data_MazeForward.Add(new DataPoint(i, sensorData.get(i).getMazeSensorForward()));
	                    data_MazeLeft.Add(new DataPoint(i, sensorData.get(i).getMazeSensorLeft()));
	                    data_MazeRight.Add(new DataPoint(i, sensorData.get(i).getMazeSensorRight()));
					}
					
					//set max values
					statusView.SetItem("Max Engine Current", maxEngineCurrent,"mA");
					
	        		//invalidate charts
	        		sensorChart_Angle.invalidate();
	    			sensorChart_Battery.invalidate();
	    			sensorChart_Engine.invalidate();
	    			sensorChart_Maze.invalidate();
	    			
	    			//close session dialog
	    			sessionDialog.dismiss();
	    			
	    			//switch to chart view
	    			mainFlipper.setDisplayedChild(3);
				}
				else
				{
					//TODO: notify user, or do something with bad session
				}
			}
		});

		GetSettings();
	}

	/**
	 * Initializes all Views
	 */
	public void InitViews()
	{	
		// ######### NETWORK RELATED ######
		mNetworkController = new NetworkController();
		mNetworkController.SetPort(1337);
		mNetworkController.SetNetListener(netListener);

		// Network ipDialog
		mDialog = new Dialog(this);
		mSettingsDialog = new Dialog(this);
		
		wifiCon = (TextView) findViewById(R.id.wifiCon);
		vehicleCon = (TextView) findViewById(R.id.networkCon);
		wifiCon.setText("WiFi connection: "
				+ (mNetworkController.isInternetOn(this) == true ? "Available"
						: "Unavailable"));
		// ######### NETWORK END  ##########

		// ########## MAIN MENU #############
		
		mainFlipper = (ViewFlipper)findViewById(R.id.mainFlipper);
		
		//load animations
		flipup_in = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipup_in);
		flipup_out = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipup_out);
		flipdown_in = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipdown_in);
		flipdown_out = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipdown_out);
		flipleft_in = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipleft_in);
		flipleft_out = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipleft_out);
		flipright_in = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipright_in);
		flipright_out = AnimationUtils.loadAnimation(mainFlipper.getContext(), R.anim.flipright_out);
		
		mMainView = (MainMenuView)findViewById(R.id.mainMenuView);
		mMainView.setOnDragListener(new MainMenuListener());
		// ######### END MAIN MENU #####################
		
		//### REPLAY ###
		
		viewFlipper = (ViewFlipper)findViewById(R.id.viewFlipper1);
		
		//Navbar Buttons
		((Button)findViewById(R.id.buttonNav_Data)).setOnClickListener(ButtonClickListener);
		((Button)findViewById(R.id.buttonNav_Chart01)).setOnClickListener(ButtonClickListener);
		((Button)findViewById(R.id.buttonNav_Chart02)).setOnClickListener(ButtonClickListener);
		((Button)findViewById(R.id.buttonNav_Chart03)).setOnClickListener(ButtonClickListener);
		((Button)findViewById(R.id.buttonNav_Chart04)).setOnClickListener(ButtonClickListener);
		
		statusView = (StatusListView)findViewById(R.id.dataListView);
		
		//### CHARTS ###
		sensorChart_Angle = (SensorChart)findViewById(R.id.sensorChart01);
		sensorChart_Battery = (SensorChart)findViewById(R.id.sensorChart02);
		sensorChart_Engine = (SensorChart)findViewById(R.id.sensorChart03);
		sensorChart_Maze = (SensorChart)findViewById(R.id.sensorChart04);
		
		data_Angle = new DataSet("Hellingshoek");
		data_Angle.SetColor(Color.GREEN);
        
		data_Battery = new DataSet("Accuspanning");
		data_Battery.SetColor(Color.RED);
		
		data_Engine_Voltage = new DataSet("Motorspanning");
		data_Engine_Voltage.SetColor(Color.CYAN);
		
		data_Engine_Current = new DataSet("Motorstroom");
		data_Engine_Current.SetColor(Color.RED);

		data_MazeForward = new DataSet("Maze Forward");
		data_MazeForward.SetColor(Color.CYAN);
		data_MazeLeft = new DataSet("Maze Left");
		data_MazeLeft.SetColor(Color.GREEN);
		data_MazeRight = new DataSet("Maze Right");
		data_MazeRight.SetColor(Color.RED);
		
		sensorChart_Angle.SetData("Hellingshoek", data_Angle);
		sensorChart_Battery.SetData("Accuspanning", data_Battery);
		sensorChart_Engine.SetData("Motorspanning", data_Engine_Voltage);
		sensorChart_Engine.SetData("Motorstroom", data_Engine_Current);
		sensorChart_Maze.SetData("Maze Forward", data_MazeForward);
		sensorChart_Maze.SetData("Maze Left", data_MazeLeft);
		sensorChart_Maze.SetData("Maze Right", data_MazeRight);
		//### END CHARTS ###
		
		
		//### END REPLAY ###
		
		//maze
		mazeSensorView = (MazeSensorView)findViewById(R.id.mazeSensorView1);
		autonomousEngine = new AutonomousEngine();
		
		// bars
		barAccuSpanning = (BarView) findViewById(R.id.BarAccuSpanning);
		barMotorSpanning = (BarView) findViewById(R.id.barMotorSpanning);

		// initialize toggle buttons
		toggleButton_Mode = (ZToggleButton) findViewById(R.id.autonoomButton);
		toggleButton_Mode.SetUnCheckedColor(Color.rgb(0, 255, 0));
		toggleButton_Mode.SetCheckedColor(Color.rgb(0, 0, 255));
		
		toggleButton_Ring = (ZToggleButton) findViewById(R.id.ringButton);
		toggleButton_Ring.SetUnCheckedColor(Color.rgb(0, 255, 0));
		toggleButton_Ring.SetCheckedColor(Color.rgb(255, 0, 0));
		
		toggleButton_AcceleroSteer = (ZToggleButton) findViewById(R.id.acceleroButton);
		toggleButton_AcceleroSteer.SetUnCheckedColor(Color.rgb(255, 255, 255));
		toggleButton_AcceleroSteer.SetCheckedColor(Color.rgb(128, 128, 128));
		
		toggleButton_Waterland = (ZToggleButton) findViewById(R.id.waterLandToggle);
		toggleButton_Waterland.SetUnCheckedColor(Color.rgb(255, 128, 0));
		toggleButton_Waterland.SetCheckedColor(Color.rgb(0, 128, 255));
		timer_StartStopToggleButton = (ToggleButton) findViewById(R.id.timer_StartStopToggleButton);
		timer_LapButton = (Button) findViewById(R.id.timer_LapButton);

		toggleButton_Mode.setWidth(150);
		toggleButton_Ring.setWidth(150);
		toggleButton_AcceleroSteer.setWidth(150);

		// set listeners
		toggleButton_AcceleroSteer.setOnClickListener(ButtonClickListener);
		timer_StartStopToggleButton.setOnClickListener(ButtonClickListener);
		timer_LapButton.setOnClickListener(ButtonClickListener);
		toggleButton_Mode.setOnClickListener(ButtonClickListener);
		toggleButton_Ring.setOnClickListener(ButtonClickListener);
		toggleButton_Waterland.setOnClickListener(ButtonClickListener);

		// input views
		vJoyStick = (JoystickView) findViewById(R.id.virtualJoystickView);
		vJoyStick.setOnJoystickMovedListener(new JoyStickHandler());

		vSteeringWheel = (SteeringView) findViewById(R.id.steeringView);
		vSteeringWheel.setOnClickListener(ButtonClickListener);
		
		acceleroHandler = new Accelerometer(this);
		acceleroHandler.setOnMoveListener(new WheelListener());

		// output views
		tiltView = (TiltView) findViewById(R.id.tiltView);
		statusListView = (StatusListView) findViewById(R.id.statusListView);
		statusListView.SetTextSize(10);
		statusListView.SetColHeader(0, "Name");
		statusListView.SetColHeader(1, "Value");

		// Timer
		timer_CurrentTimeText = (TextView) findViewById(R.id.timer_CurrentTimeText);
		timer_LapTimeText = (TextView) findViewById(R.id.timer_LapTimeText);

		timerTickTask = new TimerTickListener();
		timer = new Timer(timerTickTask, 10);

		// Emergency stop button
		button_EmergencyStop = (StopButtonView) findViewById(R.id.stopButton);
		button_EmergencyStop.setOnClickListener(ButtonClickListener);
		
		// alert dialog stuff
		alertDialog = new AlertDialog.Builder(this).create();
		
		alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
		   public void onClick(DialogInterface dialog, int which) {
		      alertDialog.dismiss();
		   }
		});
		
		tiltView.SetValue(0);
	}

	/**
	 * when the activity resumes
	 */
	@Override
	public void onResume()
	{
		acceleroHandler.register();

		super.onResume();
	}

	/**
	 * Destroy remaining systems
	 */
	@Override
	public void onPause()
	{
		// stop listening for accelerometer events
		acceleroHandler.unregister();

		// if the activity is stopping for real
		if (this.isFinishing())
		{
			// if network is still connected destroy it
			if (mNetworkController.IsConnected())
			{
				try
				{
					mNetworkController.Disconnect();
					networkInput.cancel(false);
				} catch (IOException e)
				{
					e.printStackTrace();
				}
			}
		}

		super.onDestroy();
	}

	//### NAVBAR ###
		/**
		 * Sets the specified button to it's active color
		 * @param button
		 */
		private void SetActiveButton(Button button)
		{
			//reset button color
			((Button)findViewById(R.id.buttonNav_Data)).getBackground().setColorFilter(DEFAULT_NAVCOLOR, Mode.MULTIPLY);
			((Button)findViewById(R.id.buttonNav_Chart01)).getBackground().setColorFilter(DEFAULT_NAVCOLOR, Mode.MULTIPLY);
			((Button)findViewById(R.id.buttonNav_Chart02)).getBackground().setColorFilter(DEFAULT_NAVCOLOR, Mode.MULTIPLY);
			((Button)findViewById(R.id.buttonNav_Chart03)).getBackground().setColorFilter(DEFAULT_NAVCOLOR, Mode.MULTIPLY);
			((Button)findViewById(R.id.buttonNav_Chart04)).getBackground().setColorFilter(DEFAULT_NAVCOLOR, Mode.MULTIPLY);
			
			//set active button color
			button.getBackground().setColorFilter(SELECTED_NAVCOLOR, Mode.MULTIPLY);
		}
		
		/**
		 * Flips the viewFlipper(holding charts) to the given index, and sets the right animations
		 * @param viewIndex
		 */
		private void FlipView(int viewIndex)
		{
			if(viewFlipper.getDisplayedChild() > viewIndex)
			{
				viewFlipper.setInAnimation(flipup_in);
				viewFlipper.setOutAnimation(flipup_out);
				viewFlipper.setDisplayedChild(viewIndex);
			}
			else if(viewFlipper.getDisplayedChild() < viewIndex)
			{
				viewFlipper.setInAnimation(flipdown_in);
				viewFlipper.setOutAnimation(flipdown_out);
				viewFlipper.setDisplayedChild(viewIndex);
			}
		}
		//### END NAVBAR ###
	
	/**
	 * Handle network events
	 */
	protected NetListener netListener = new NetListener()
	{	
		public void onNetEvent(NetEvent event)
		{
			switch (event.getEventID())
			{
				case TYPE_CON:
				{
					if (conDialog != null)
						conDialog.dismiss();

					// show connected status
					vehicleCon.setText("Vehicle: Connected");
					
					networkInput = new NetworkInput();
					networkInput.execute();
					
					// create a sessions
					idpSession = idpDatabase.CreateSession();
					mDataServer = new DataServer();
					mDataServer.Start();
					
					// if we are connected we want to show the dashboard
					mainFlipper.setDisplayedChild(1);
				}break;
				
				case TYPE_DISCON:
				{
					// try to disconnect properly
					try
					{
						mNetworkController.Disconnect();
						networkInput.cancel(false);
					} catch (IOException e)
					{
						e.printStackTrace();
					}
					
					alertDialog.setMessage(event.getMsg());
					alertDialog.show();

					// set button to connect
					vehicleCon.setText("Vehicle: Disconnected");
					
					// show the main menu
					mainFlipper.setDisplayedChild(0);
					
					long end = (new Date()).getTime();
					idpSession.setEndTime(end);
					idpDatabase.SetSessionEndTime(idpSession);
					idpSession = null;
					mDataServer.terminate();
				}break;
				
				case TYPE_RECEIVE:
				{
                    if( (event.getMsgID() & 0xFF) == ProtocolOpcodes.V_SENSOR.Opcode() )
                    {
                        byte[] data = event.getData();
                        
                        // fill data in the structure
                        currentSensorData.setAccuspanning( data[0] & 0xFF );
                        currentSensorData.setMotorspanning( data[1] & 0xFF );
                        currentSensorData.setMotorstroom( data[2] & 0xFF );
                        currentSensorData.setHellingshoek( (data[3] & 0xFF) - 60 );
                        currentSensorData.setMazeSensors( (short) (data[4] & 0xFF), (short) (data[5] & 0xFF), (short) (data[6] & 0xFF));
                        
                        // update views
                        tiltView.SetValue(currentSensorData.getHellingshoek());
                        barAccuSpanning.SetValue( (int) (currentSensorData.getAccuspanning() / 15.0f * 100.0f));
                        barMotorSpanning.SetValue((int) (currentSensorData.getMotorspanning() / 24.0f * 100.0f));
                        mazeSensorView.SetValues(currentSensorData.getMazeSensorForward(), currentSensorData.getMazeSensorLeft(), currentSensorData.getMazeSensorRight());
                        statusListView.SetItem("Engine Current", currentSensorData.getMotorstroom(), "mA");
                        
                        // do autonomous stuf
                        if(AutoMode)
                        {
                        	//send current sensor values to autonomous engine
                        	autonomousEngine.SetSensors(currentSensorData.getMazeSensorForward(), currentSensorData.getMazeSensorRight(), currentSensorData.getMazeSensorLeft());
                        	
                        	//reads result from autonomous engine
                        	driveValue = (byte) Clamp(autonomousEngine.GetDrive(), 0 , 254);
                        	steerValue = (byte) Clamp(autonomousEngine.GetSteer(), 0 , 254);
                        	
                        	//set statusview values
                        	statusListView.SetItem("AutoDrive", driveValue);
                        	statusListView.SetItem("AutoSteer", steerValue);
                        }
                        
                        // do database stuff
                        if(idpSession != null)
                        {
                        	idpDatabase.AddSensorData(idpSession, currentSensorData);
                        	
                        	if(mDataServer.ClientsConnected())
                        	{
                        		mDataServer.SendData(currentSensorData);
                        	}
                        }
                    }
                    
				}break;
				
				case TYPE_NOTIFI:
				{
					if (conDialog != null)
						conDialog.dismiss();
					
					alertDialog.setMessage(event.getMsg());
					alertDialog.show();
				}break;
			}
		}
	};
	

	/**
	 * Click listener for all buttons
	 */
	protected OnClickListener ButtonClickListener = new OnClickListener()
	{
		public void onClick(View view)
		{
			switch (view.getId())
			{
				case R.id.ipSubmit:
				{
					// get the data from the dialog
					mNetworkController.SetIpAddress(IpInput.getText().toString());
					mNetworkController.SetPort(Integer.parseInt( port.getText().toString()));
					
					// close the dialog
					mDialog.dismiss();
					
					//ditch the bloody keyboard!
					InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
				    imm.hideSoftInputFromWindow(mainFlipper.getApplicationWindowToken(), 0);

					if(IpInput.getText().toString().equals("1234"))
					{
						mainFlipper.setDisplayedChild(1);
					}
					else
					{
						// show trying to connect dialog
						conDialog = ProgressDialog.show(view.getContext(), "",
								"Trying to connect to "
								+ IpInput.getText().toString()+":"+port.getText().toString(), false);
	
						// try to connect
						mNetworkController.Connect();
					}
				}
					break;
					
				case R.id.btnSave:
				{
					// do checks
					String ip = settingsIP.getText().toString();
					if(ip.length() == 0)
						ip = DEFAULT_IP;
					
					int port = Integer.parseInt(settingsPort.getText().toString());
					if (port < 0 || port > 65535)
						port = DEFAULT_PORT;
					
					
					// save the settings
					SaveSettings(ip, port);
					
					
					// dismiss the dialog
					mSettingsDialog.dismiss();

				}break;

				case R.id.ringButton:
				{
					if(mNetworkController.IsConnected())
					{
						mNetworkController.Send(ProtocolEncoder.EncodeCommand((byte) 1));
					}
				}
					break;
					
				case R.id.stopButton:
				{				
					if(mNetworkController.IsConnected())
					{
						mNetworkController.Send(ProtocolEncoder.EncodeCommand((byte) 2));
					}
				}
					break;

				case R.id.waterLandToggle:
				{
					if(mNetworkController.IsConnected())
					{
						mNetworkController.Send(ProtocolEncoder.EncodeCommand((byte) 3));
					}
				}
					break;
					
				case R.id.autonoomButton:
				{
					if(mNetworkController.IsConnected())
					{
						mNetworkController.Send(ProtocolEncoder.EncodeCommand((byte) 4));
						AutoMode = !AutoMode;
					}
				}
					break;

				case R.id.acceleroButton:
				{
					useAcceleroSteering = ((ToggleButton)view).isChecked();
					vJoyStick.SetAllowX(!useAcceleroSteering);
					steerValue = 127;
					driveValue = 127;
				}
					break;

				case R.id.steeringView:
				{
					useAcceleroSteering = !useAcceleroSteering;
					vJoyStick.SetAllowX(!useAcceleroSteering);
				}
					break;

				case R.id.timer_StartStopToggleButton:
				{
					if (timer_StartStopToggleButton.isChecked()
							&& !timer.IsStarted())
					{
						timer.start();
					}
					else
					{
						timer.stop();
					}
				}
					break;

				case R.id.timer_LapButton:
				{
					timer_LapTimeText.setText(Timer.ParseMillisecs(timer
							.ElapsedTime()));
					statusListView.SetItem("Lap Time",
							Timer.ParseMillisecs(timer.ElapsedTime()) + "");
				}
					break;
				case R.id.btnClearDB:
				{
					idpDatabase.ClearDB(view.getContext());
					idpDatabase = new DatabaseHelper(view.getContext());
				}break;
				
				//###REPLAY###
				case R.id.buttonNav_Data:
					FlipView(0);
					SetActiveButton(((Button)view));
					break;
				case R.id.buttonNav_Chart01:
					FlipView(1);
					SetActiveButton(((Button)view));
					break;
				case R.id.buttonNav_Chart02:
					FlipView(2);
					SetActiveButton(((Button)view));
					break;
				case R.id.buttonNav_Chart03:
					FlipView(3);
					SetActiveButton(((Button)view));
					break;
				case R.id.buttonNav_Chart04:
					FlipView(4);
					SetActiveButton(((Button)view));
					break;
				//###END REPLAY###
			}
		}
	};

	/**
	 * Timer ticker
	 * 
	 */
	protected class TimerTickListener implements TimerListener
	{
		public void OnTick(long elapsedMilliSeconds)
		{
			timer_CurrentTimeText.setText(Timer
					.ParseMillisecs(elapsedMilliSeconds));
			statusListView.SetItem("Current Race Time",
					Timer.ParseMillisecs(elapsedMilliSeconds));
		}
	}

	/**
	 * Accelerometer listener
	 * 
	 */
	protected class WheelListener implements AccelerometerListener
	{
		public void onMoved(float pitch, float yaw, float roll)
		{
			statusListView.SetItem("Accelerometer Pitch", (pitch * 90) + "");
			
			if(useAcceleroSteering)
			{
				// if we're using accelerometer steering, set steeringview
				if (vSteeringWheel != null)
				{
					vSteeringWheel.setValue(pitch * 90);
				
					// convert values to byte
					steerValue = (byte) Clamp((-pitch + 1.0f) * 128f, 1 , 254);
					statusListView.SetItem("SteeringValue", (steerValue & 0xFF) + "");
				
					// convert values to byte
					driveValue = (byte)  Clamp((-roll + 1.0f) * 128f, 1 , 254);
					statusListView.SetItem("DriveValue", (driveValue & 0xFF) + "");	
				}
			}
		}
	}

	/**
	 * Joystick handler
	 * 
	 */
	public class JoyStickHandler implements JoystickListener
	{
		public void OnMoved(float pan, float tilt)
		{
//			// start the timer as soon as we hit the gas!
//			if (!timer.IsStarted() && tilt > 1)
//			{
//				timer_StartStopToggleButton.setChecked(true);
//				timer.start();
//			}

			// if we're using joystick steering, set the steering view
			if(!useAcceleroSteering)
			{
				// set steeringview
				if (vSteeringWheel != null)
					vSteeringWheel.setValue(-pan * 90.0f);
				
				// convert values to byte
				steerValue = (byte) Clamp((pan + 1.0f) * 128f, 1 , 254);
				statusListView.SetItem("SteeringValue", (steerValue & 0xFF) + "");

				// convert values to byte
				driveValue = (byte)  Clamp((-tilt + 1.0f) * 128f, 1 , 254);
				statusListView.SetItem("DriveValue", (driveValue & 0xFF) + "");
			}

			statusListView.SetItem("Joystick (pan, tilt)", pan + "," + tilt);
		}

		public void OnReleased()
		{
			if(!useAcceleroSteering)
			{
				if (vSteeringWheel != null)
					vSteeringWheel.setValue(0);
				
				steerValue = 127;
				driveValue = 127;
				
				statusListView.SetItem("DriveValue", 127+"");
				statusListView.SetItem("SteeringValue", 127+"");
			}			
		}
	}

	/**
	 * Handles main menu events
	 *
	 */
	protected class MainMenuListener implements MenuDragListener
	{
		public void onMenuItemSelected(MainMenuItem menuItem)
		{
			switch(menuItem)
			{
				case Settings:
				{
					mSettingsDialog.setContentView(R.layout.settingsdialog);
					mSettingsDialog.setTitle("Settings eigen voertuig");
					mSettingsDialog.findViewById(R.id.btnSave).setOnClickListener(ButtonClickListener);
					((Button)mSettingsDialog.findViewById(R.id.btnClearDB)).setOnClickListener(ButtonClickListener);

					settingsIP = (EditText)mSettingsDialog.findViewById(R.id.txtSettingsIP);
					settingsPort = (EditText)mSettingsDialog.findViewById(R.id.txtSettingsPort);
					//settingsMac = (EditText)mSettingsDialog.findViewById(R.id.txtSettingsMAC);
					
					if(ipAddress != "")
					{
						settingsIP.setText(ipAddress);
					}
					
					if(portNumber != 0)
					{
						settingsPort.setText("" +portNumber);
					}
						
					mSettingsDialog.show();
				}break;
				
				case Replay:
				{
					List<Session> sesList = idpDatabase.GetSessions();
					
					if(sesList != null)
					{
						sessionListView.setAdapter(new SessionAdapter(context, R.id.sessionList, sesList));
					}
					else
					{
						sessionListView.setAdapter(new SessionAdapter(context, R.id.sessionList, new ArrayList<Session>()));
					}
					
					sessionDialog.show();
				}break;
				
				case BruteForce:
				{
					if (!mNetworkController.IsConnected())
						{
							mDialog.setContentView(R.layout.ipdialog);
							mDialog.setTitle("Enter IP & Port");
							
							IpInput = (EditText) mDialog.findViewById(R.id.ipAddress);
							port = (EditText)mDialog.findViewById(R.id.port);
							
							if(ipAddress != "")
							{
								IpInput.setText(ipAddress);
							}
							
							if(portNumber != 0)
							{
								port.setText(""+portNumber);
							}
							
							mDialog.findViewById(R.id.ipSubmit).setOnClickListener(ButtonClickListener);
							mDialog.show();
						}
				}break;
				
				case OwnVehicle:
				{
					//get the data from the dialog
					mNetworkController.SetIpAddress(ipAddress);
					mNetworkController.SetPort(portNumber);
					
					// show trying to connect dialog
					conDialog = ProgressDialog.show(mContext, "",
							"Trying to connect to "
							+ ipAddress +":"+portNumber, false);

					// try to connect
					mNetworkController.Connect();
					networkInput = new NetworkInput();
					networkInput.execute();
				}break;
			}
		}
	}

	/**
	 * Key down event handler
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		if (keyCode == KeyEvent.KEYCODE_BACK)
		{
			if(mainFlipper.getDisplayedChild() != 0)
			{
				if(mainFlipper.getDisplayedChild() != 1 || !mNetworkController.IsConnected())
					mainFlipper.setDisplayedChild(0);
				else
				{
					if(mNetworkController.IsConnected())
					{
						try
						{
							long end = (new Date()).getTime();
							idpSession.setEndTime(end);
							idpDatabase.SetSessionEndTime(idpSession);
							idpSession = null;
							mDataServer.terminate();
							
							networkInput.cancel(false);
							networkInput = null;
							mNetworkController.Disconnect();

						}
						catch (IOException e)
						{
							e.printStackTrace();
						}

						alertDialog.setMessage("Disconnected from the vehicle!");
						alertDialog.show();
						
						mainFlipper.setDisplayedChild(0);
					}
				}
			}
			else
			{
				System.runFinalizersOnExit(true);
				System.exit(0);
			}
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
	
	/**
	 * Get the saved settings
	 */
	public void GetSettings()
	{
		SharedPreferences mPrefs = getSharedPreferences("AmfibiSettings", 0);
		
		ipAddress = mPrefs.getString("IP", DEFAULT_IP);
		//macAddress = mPrefs.getString("MAC", DEFAULT_MAC);
		portNumber = mPrefs.getInt("port", DEFAULT_PORT);
	}
	
	/**
	 * Save the settings
	 * @param ip
	 * @param mac
	 * @param port
	 */
	public void SaveSettings(String ip, int port)
	{
		SharedPreferences mPrefs = getSharedPreferences("AmfibiSettings", 0);
        SharedPreferences.Editor ed = mPrefs.edit();
        
		ipAddress = ip;
		//macAddress = mac;
		portNumber = port;
        
		ed.putString("IP", ip);
		//ed.putString("MAC", mac);
        ed.putInt("port", port);
        
        ed.commit();
	}
	
	/**
	 * sends the input to the vehicle
	 * @author MarkLaptop
	 *
	 */
	public class NetworkInput extends AsyncTask<Void, Void, Void>
	{
		private byte lastSteerValue;
		private byte lastDriveValue; 
		
		
		@Override
		protected Void doInBackground(Void... params) 
		{	
			mCurrentTime = System.currentTimeMillis();
			mLastTime = mCurrentTime;
			mInputTimer = 0;
			
			while( !this.isCancelled() )
			{
				if(mNetworkController.IsConnected())
				{
		            // get the deltaTime
		            mCurrentTime = System.currentTimeMillis();
		            mDeltaTime = mCurrentTime - mLastTime;
		            mInputTimer += mDeltaTime;
		            
		            mNetworkController.CheckQueue();
		            
		            if( mInputTimer > INPUT_INTERVAL)
		            {
						// send stuff to the vehicle
		            	if(lastSteerValue != steerValue)
		            		mNetworkController.Send( ProtocolEncoder.EncodeTurn(steerValue) );
		            	
		            	if(lastDriveValue != driveValue)
		            		mNetworkController.Send( ProtocolEncoder.EncodeDrive(driveValue) );
						
						// reset the timer
		            	mInputTimer -= INPUT_INTERVAL;
		            	
		            	// set last values
		            	lastSteerValue = steerValue;
		            	lastDriveValue = driveValue;
		            }
		            
		            mLastTime = mCurrentTime;
				}
			}
			
			return null;
		}
	}
	
	/**
	 * For clamping values

	 * @return clamped value
	 */
	private float Clamp(float value, float min, float max)
	{
		return (value < min) ? min : (value > max) ? max : value;
	}
}